//
//  UIImage+DDAdd.m
//  Pods
//
//  Created by 许龙 on 2019/9/26.
//

#import "UIImage+DDAdd.h"
//#import <Accelerate/Accelerate.h>

@implementation UIImage (DDAdd)

#pragma mark - Create image

+ (nullable UIImage *)imageWithColor:(UIColor *)color {
    return [self imageWithColor:color size:CGSizeMake(1, 1)];
}

+ (nullable UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
    if (!color || size.width <= 0 || size.height <= 0) return nil;
    CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

+ (nullable UIImage *)imageNamed:(NSString *)name backgroundColor:(UIColor *)color size:(CGSize)size {
    if (!name.length || !color || size.width <= 0 || size.height <= 0) return nil;
    UIImage *image = [UIImage imageNamed:name];
    if (!image) return nil;
    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
    [image drawInRect:CGRectMake((size.width - image.size.width)/2, (size.height - image.size.height)/2, image.size.width, image.size.height)];
    UIImage *rt = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return rt;
}

+ (nullable UIImage *)imageWithSize:(CGSize)size drawBlock:(void (^)(CGContextRef context))drawBlock {
    if (!drawBlock) return nil;
    if (size.width <= 0 || size.height <= 0) return nil;
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    if (!context) return nil;
    drawBlock(context);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

+ (UIImage *)imageWithUnicode:(NSString *)unicode
                     fontName:(NSString *)fontName
                     fontSize:(CGFloat)fontSize
                        color:(UIColor *)color {
   
    if([unicode isEqualToString:@"widget"]){// lqytip[12.9.4] -  兼容iOS小组件中的字体图片获取
        unicode = @"\U0000eaf5";
    }
    
    CGFloat scale = [UIScreen mainScreen].scale;
    UIFont *font = [UIFont fontWithName:fontName size:fontSize * scale];
    if (!font) return nil;
    
    CGSize size = [unicode sizeWithAttributes:@{ NSFontAttributeName : font }];
    if (size.width == 0 || size.height == 0) return nil;

    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
        
    if ([unicode respondsToSelector:@selector(drawAtPoint:withAttributes:)]) {
        [unicode drawAtPoint:CGPointZero
              withAttributes:@{ NSFontAttributeName : font, NSForegroundColorAttributeName : color }];
    } else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        CGContextSetFillColorWithColor(context, color.CGColor);
        [unicode drawAtPoint:CGPointMake(0, 0) withFont:font];
#pragma clang diagnostic pop
    }
    
    UIImage *image = [UIImage imageWithCGImage:UIGraphicsGetImageFromCurrentImageContext().CGImage scale:scale orientation:UIImageOrientationUp];
    UIGraphicsEndImageContext();
    return image;
}

+ (nullable UIImage *)imageWithQRCode:(NSString *)qrcode size:(CGFloat)size {
    if (!qrcode || !qrcode.length) return nil;
    NSData *data = [qrcode dataUsingEncoding:NSUTF8StringEncoding];
    if (!data) return nil;
    
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [filter setDefaults];
    [filter setValue:data forKey:@"inputMessage"];
    CIImage *outputImage = [filter outputImage];
    if (!outputImage) return nil;
    
    return [self createNonInterpolatedUIImageFromCIImage:outputImage size:size];
}

+ (UIImage *)createNonInterpolatedUIImageFromCIImage:(CIImage *)image size:(CGFloat)size {
    CGRect extent = CGRectIntegral(image.extent);
    CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
    
    // 1.创建bitmap
    size_t width = CGRectGetWidth(extent)*scale;
    size_t height = CGRectGetHeight(extent)*scale;
    
    CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
    CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
    CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
    CGContextScaleCTM(bitmapRef, scale, scale);
    CGContextDrawImage(bitmapRef, extent, bitmapImage);
    
    //2.保存bitmap到图片
    CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
    CGContextRelease(bitmapRef);
    CGImageRelease(bitmapImage);
    
    return [UIImage imageWithCGImage:scaledImage];
}

#pragma mark - Image Info

- (BOOL)hasAlphaChannel {
    if (!self.CGImage) return NO;
    CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage);
    return (alpha == kCGImageAlphaFirst ||
            alpha == kCGImageAlphaLast ||
            alpha == kCGImageAlphaPremultipliedFirst ||
            alpha == kCGImageAlphaPremultipliedLast);
}

#pragma mark - Modify Image

- (nullable UIImage *)imageByAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
    CGFloat radians = angle / 180 * M_PI;
    return [self imageByRotate:radians fitSize:fitSize];
}

- (nullable UIImage *)imageByRotate:(CGFloat)radians fitSize:(BOOL)fitSize {
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    CGAffineTransform trans = fitSize ? CGAffineTransformMakeRotation(radians) : CGAffineTransformIdentity;
    CGRect newRect = CGRectApplyAffineTransform(CGRectMake(0.f, 0.f, width, height), trans);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 (size_t)newRect.size.width,
                                                 (size_t)newRect.size.height,
                                                 8,
                                                 (size_t)newRect.size.width * 4,
                                                 colorSpace,
                                                 kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorSpace);
    if (!context) return nil;
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
    CGContextRotateCTM(context, radians);
    CGContextTranslateCTM(context, -(width * 0.5), -(height * 0.5));
    
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
    CGImageRef imgRef = CGBitmapContextCreateImage(context);
    UIImage *image = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
    
    CGImageRelease(imgRef);
    CGContextRelease(context);
    return image;
}

- (UIImage *)imageByFixedTakePhotoOrientation {
    if (self.imageOrientation == UIImageOrientationUp) return self;
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;
    switch (self.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, self.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }
    
    switch (self.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }
    
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
                                             CGImageGetBitsPerComponent(self.CGImage), 0,
                                             CGImageGetColorSpace(self.CGImage),
                                             CGImageGetBitmapInfo(self.CGImage));
    CGContextConcatCTM(ctx, transform);
    
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
            break;
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
            break;
    }
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    
    return img;
}

- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius {
    return [self imageByRoundCornerRadius:radius borderWidth:0 borderColor:nil];
}

- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius
                          borderWidth:(CGFloat)borderWidth
                          borderColor:(UIColor *)borderColor {
    return [self imageByRoundCornerRadius:radius
                                  corners:UIRectCornerAllCorners
                              borderWidth:borderWidth
                              borderColor:borderColor
                           borderLineJoin:kCGLineJoinRound];
}

- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius
                              corners:(UIRectCorner)corners
                          borderWidth:(CGFloat)borderWidth
                          borderColor:(nullable UIColor *)borderColor
                       borderLineJoin:(CGLineJoin)borderLineJoin {
    if (corners != UIRectCornerAllCorners) {
        UIRectCorner tmp = 0;
        if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerTopLeft;
        if (corners & UIRectCornerTopRight) tmp |= UIRectCornerTopRight;
        if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerBottomLeft;
        if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerBottomRight;
        corners = tmp;
    }
    if (self.size.width <= 0 || self.size.height <= 0) return nil;
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextTranslateCTM(context, 0, rect.size.height);
    CGContextScaleCTM(context, 1, -1);
    
    CGFloat minSize = MIN(self.size.width, self.size.height);
    if (borderWidth < minSize / 2) {
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth)
                                                   byRoundingCorners:corners
                                                         cornerRadii:CGSizeMake(radius, borderWidth)];
        path.lineWidth = borderWidth;
        [path closePath];
        
        if (borderColor) {
            path.lineJoinStyle = borderLineJoin;
            [borderColor setStroke];
            [path stroke];
        }
        
        CGContextSaveGState(context);
        [path addClip];
        CGContextDrawImage(context, rect, self.CGImage);
        CGContextRestoreGState(context);
    }
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (nullable UIImage *)imageByResizeToSize:(CGSize)size {
    if (size.width <= 0 || size.height <= 0) return nil;
    UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (nullable UIImage *)imageByCropToRect:(CGRect)rect {
    rect.origin.x *= self.scale;
    rect.origin.y *= self.scale;
    rect.size.width *= self.scale;
    rect.size.height *= self.scale;
    if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
    CGImageRef imgRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *image = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imgRef);
    return image;
}

#pragma mark - Image Effect

- (nullable UIImage *)imageByMaskImage:(UIImage *)maskImage rect:(CGRect)rect {
    if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
    if (!maskImage) return nil;
    CGSize size = self.size;
    UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    [maskImage drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (UIImage *)imageWithAppendImage:(UIImage *)image vertical:(BOOL)vertical offset:(CGFloat)offset {
    CGFloat w = vertical ? MAX(self.size.width, image.size.width) : self.size.width + image.size.width + offset;
    CGFloat h = vertical ? self.size.height + image.size.height + offset : MAX(self.size.height, image.size.height);
    
    if (w <= 0 || h <= 0) return nil;
    
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(w, h), NO, self.scale);
    [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
    [image drawInRect:CGRectMake(vertical ? 0 : self.size.width + offset,
                                 vertical ? self.size.height + offset : 0,
                                 image.size.width,
                                 image.size.height)];
    UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resultingImage;
}

//- (nullable UIImage *)imageByBlurRadius:(CGFloat)blurRadius {
//    if (blurRadius < 0 || blurRadius > 1) return nil;
//    // Create a box size for vImageBoxConvolve_ARGB8888
//    // box size must be odd:
//    // https://developer.apple.com/documentation/accelerate/1515945-vimageboxconvolve_argb8888?language=objc
//    int boxSize = (int)(blurRadius * 100);
//    boxSize -= boxSize % 2 + 1;
//
//    CGImageRef imgRef = self.CGImage;
//    vImage_Buffer inBuffer, outBuffer;
//    CGDataProviderRef inProvider = CGImageGetDataProvider(imgRef);
//    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
//
//    inBuffer.width = CGImageGetWidth(imgRef);
//    inBuffer.height = CGImageGetHeight(imgRef);
//    inBuffer.rowBytes = CGImageGetBytesPerRow(imgRef);
//    inBuffer.data = (void *)CFDataGetBytePtr(inBitmapData);
//
//    void *pixelBuffer = malloc(CGImageGetBytesPerRow(imgRef) * CGImageGetHeight(imgRef));
//    if (pixelBuffer == NULL) return nil;
//    outBuffer.width = CGImageGetWidth(imgRef);;
//    outBuffer.height = CGImageGetHeight(imgRef);
//    outBuffer.rowBytes = CGImageGetBytesPerRow(imgRef);
//    outBuffer.data = pixelBuffer;
//
//    vImage_Error error = vImageBoxConvolve_ARGB8888(&inBuffer,
//                                                    &outBuffer,
//                                                    NULL,
//                                                    0,
//                                                    0,
//                                                    (uint32_t)boxSize,
//                                                    (uint32_t)boxSize,
//                                                    NULL,
//                                                    kvImageEdgeExtend);
//    if (error != kvImageNoError) return nil;
//    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//    CGContextRef ctx = CGBitmapContextCreate(
//                                             outBuffer.data,
//                                             outBuffer.width,
//                                             outBuffer.height,
//                                             8,
//                                             outBuffer.rowBytes,
//                                             colorSpace,
//                                             CGImageGetBitmapInfo(imgRef));
//    CGColorSpaceRelease(colorSpace);
//
//    CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
//    UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
//    CGContextRelease(ctx);
//    CGImageRelease(imageRef);
//    free(pixelBuffer);
//    CFRelease(inBitmapData);
//    return image;
//}

#pragma mark - Compress

- (nullable NSData *)dataOfCompressedToMaxByte:(NSUInteger)maxByte {
    // Compress by quality
    UIImage *image = self;
    CGFloat compression = 1;
    NSData *data = UIImageJPEGRepresentation(image, compression);
    if (data.length < maxByte) return data;
    CGFloat max = 1;
    CGFloat min = 0;
    for (int i = 0; i < 6; ++i) {  // Up to 6 times
        compression = (max + min) / 2;
        data = UIImageJPEGRepresentation(image, compression);
        if (data.length < maxByte * 0.9) {
            min = compression;
        } else if (data.length > maxByte) {
            max = compression;
        } else {
            break;
        }
    }
    UIImage *resultImage = [UIImage imageWithData:data];
    if (data.length < maxByte) return data;
    // Compress by size
    NSUInteger lastDataLength = 0;
    while (data.length > maxByte && data.length != lastDataLength) {
        lastDataLength = data.length;
        CGFloat ratio = (CGFloat)maxByte / data.length;
        CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio)));
        
        UIGraphicsBeginImageContext(size);
        [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
        resultImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        data = UIImageJPEGRepresentation(resultImage, compression);
    }
    return data;
}

- (nullable UIImage *)imageByCompressedToMaxByte:(NSUInteger)maxByte {
    NSData *data = [self dataOfCompressedToMaxByte:maxByte];
    if (!data) return nil;
    return [UIImage imageWithData:data];
}


- (nullable UIImage *)imageByResizeFitWidthToSize:(CGSize)size {
    CGFloat imageWidth = self.size.width;
    CGFloat imageHeight = self.size.height;
    
    CGFloat sizeWidth = size.width;
    CGFloat sizeHeight = size.height;
    
    if (imageHeight == 0 || sizeHeight == 0) {
        return self;
    }
    
    CGFloat ratio = sizeWidth/sizeHeight;
    
    imageHeight = imageWidth/ratio;
    
    return [self imageByCropToRect:CGRectMake(0, 0, imageWidth, imageHeight)];
    
}

@end
